package com.yami.shop.admin.controller;

import cn.hutool.cache.CacheUtil;
import cn.hutool.cache.impl.TimedCache;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.anji.captcha.model.common.ResponseModel;
import com.anji.captcha.model.vo.CaptchaVO;
import com.anji.captcha.service.CaptchaService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.yami.shop.admin.config.AdminAppGlobals;
import com.yami.shop.admin.config.AuthConst;
import com.yami.shop.admin.domain.dto.TempTokenEntity;
import com.yami.shop.common.exception.YamiShopBindException;
import com.yami.shop.common.response.ServerResponseEntity;
import com.yami.shop.common.util.R;
import com.yami.shop.common.util.TokenGenerator;
import com.yami.shop.security.admin.dto.CaptchaAuthenticationDTO;
import com.yami.shop.security.common.bo.UserInfoInTokenBO;
import com.yami.shop.security.common.enums.SysTypeEnum;
import com.yami.shop.security.common.manager.PasswordCheckManager;
import com.yami.shop.security.common.manager.PasswordManager;
import com.yami.shop.security.common.manager.TokenStore;
import com.yami.shop.security.common.vo.TokenInfoVO;
import com.yami.shop.sys.constant.Constant;
import com.yami.shop.sys.model.SysMenu;
import com.yami.shop.sys.model.SysUser;
import com.yami.shop.sys.service.SysMenuService;
import com.yami.shop.sys.service.SysUserService;
import com.yami.shop.utils.GoogleAuthenticator;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import jakarta.validation.Valid;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author FrozenWatermelon
 * @date 2020/6/30
 */
@RestController
@Tag(name = "登录")
public class AdminLoginController {
    private static final Logger logger = LoggerFactory.getLogger(AdminLoginController.class);
    @Autowired
    private TokenStore tokenStore;

    @Autowired
    private SysUserService sysUserService;

    @Autowired
    private SysMenuService sysMenuService;

    @Autowired
    private PasswordCheckManager passwordCheckManager;

    @Autowired
    private CaptchaService captchaService;

    @Autowired
    private PasswordManager passwordManager;

    @Autowired
    private GoogleAuthenticator googleAuthenticator;

    private Long timeOut = DateUnit.MINUTE.getMillis() * 3;

    private TimedCache<String, SysUser> loginMap = CacheUtil.newTimedCache(timeOut);

    private TimedCache<String, TempTokenEntity> loginTempTokenMap = CacheUtil.newTimedCache(DateUnit.MINUTE.getMillis() * 10);

    private static TimedCache<String, String> checkFormCache = CacheUtil.newTimedCache(DateUnit.SECOND.getMillis() * 10);

    @PostMapping("/adminLogin")
    @Operation(summary = "账号密码 + 验证码登录(用于后台登录)" , description = "通过账号/手机号/用户名密码登录")
    public ServerResponseEntity<?> login(
            @Valid @RequestBody CaptchaAuthenticationDTO captchaAuthenticationDTO) {
        // 登陆后台登录需要再校验一遍验证码
        CaptchaVO captchaVO = new CaptchaVO();
        captchaVO.setCaptchaVerification(captchaAuthenticationDTO.getCaptchaVerification());
        ResponseModel response = captchaService.verification(captchaVO);
        if (!response.isSuccess()) {
            return ServerResponseEntity.showFailMsg("验证码有误或已过期");
        }

        SysUser sysUser = sysUserService.getByUserName(captchaAuthenticationDTO.getUserName());
        if (sysUser == null) {
            throw new YamiShopBindException("账号或密码不正确");
        }

        // 半小时内密码输入错误十次，已限制登录30分钟
        String decryptPassword = passwordManager.decryptPassword(captchaAuthenticationDTO.getPassWord());
        //passwordCheckManager.checkPassword(SysTypeEnum.ADMIN,captchaAuthenticationDTO.getUserName(), decryptPassword, sysUser.getPassword());
        logger.info("admin1开发环境关闭密码判断：{}", AdminAppGlobals.active);
        if(AdminAppGlobals.active_dev.contains(AdminAppGlobals.active)){
            logger.info("admin2开发环境关闭密码判断：{}",AdminAppGlobals.active);
        }else{
            // 半小时内密码输入错误十次，已限制登录30分钟
            passwordCheckManager.checkPassword(SysTypeEnum.ADMIN,captchaAuthenticationDTO.getUserName(), decryptPassword, sysUser.getPassword());
        }

        // 不是店铺超级管理员，并且是禁用状态，无法登录
        if (Objects.equals(sysUser.getStatus(),0)) {
            // 未找到此用户信息
            throw new YamiShopBindException("未找到此用户信息");
        }

        logger.warn("当前环境为：{},二次验证是否开启：{}", AdminAppGlobals.active,AdminAppGlobals.gauth);
        if(!AdminAppGlobals.gauth){
            // 存储token返回vo
            UserInfoInTokenBO userInfoInToken = new UserInfoInTokenBO();
            userInfoInToken.setUserId(String.valueOf(sysUser.getUserId()));
            userInfoInToken.setSysType(SysTypeEnum.ADMIN.value());
            userInfoInToken.setEnabled(sysUser.getStatus() == 1);
            userInfoInToken.setPerms(getUserPermissions(sysUser.getUserId()));
            userInfoInToken.setNickName(sysUser.getUsername());
            userInfoInToken.setShopId(sysUser.getShopId());
            // 存储token返回vo
            TokenInfoVO tokenInfoVO = tokenStore.storeAndGetVo(userInfoInToken);
            return ServerResponseEntity.success(tokenInfoVO);
        }
        //生成一个临时Token 用户并临时存储 后续验证使用
        String tempToken = TokenGenerator.generateValue();
        TempTokenEntity tempTokenEntity = new TempTokenEntity();
        tempTokenEntity.setTempToken(tempToken);
        tempTokenEntity.setTempExpireTime(DateUtil.offsetMinute(new Date(),3));
        tempTokenEntity.setTagNum(sysUser.getTagNum());
        loginMap.put(tempToken,sysUser,timeOut);
        loginTempTokenMap.put(tempToken,tempTokenEntity,timeOut);
        return ServerResponseEntity.success(tempTokenEntity);


    }

    @ResponseBody
    @RequestMapping("/loginGenerateCode")
    public R generateCode(HttpServletRequest request) {
        String dynamicAuthToken = request.getParameter(AuthConst.DYNAMIC_AUTH_TOKEN_KEY);
        if(!loginMap.containsKey(dynamicAuthToken)){
            logger.warn("请先登录后在进行二次验证");
            return R.error("请先登录");
        }
        SysUser sysUser = loginMap.get(dynamicAuthToken,false);
        if (sysUser == null) {
            logger.warn("账号不存在");
            return R.error("账号不存在");
        }
        if(sysUser.getTagNum().longValue()<1){
            //防止直接访问该链接导致二次验证被解绑，而被非法操作绑定
            String secret = googleAuthenticator.generateSecretKey();//生成的key赋值给secret
            SysUser updateSysUser = new SysUser();
            updateSysUser.setTagKey(secret);
            String qrBarcode = googleAuthenticator.getQRBarcode(sysUser.getUsername(),secret);
            sysUserService.update(updateSysUser,new LambdaQueryWrapper<SysUser>().eq(SysUser::getUserId,sysUser.getUserId()));
            return R.ok().put("authUrl", qrBarcode);
        }
        return R.ok().put("error", "[非法操作]已经登录过，解绑需要管理员操作！");
    }

    /**
     * 二次验证登录
     * @param request
     * @return
     */
    @PostMapping("/glogin")
    public R authLogin(HttpServletRequest request, HttpServletResponse response) {
        long authCode;
        try {
            authCode = Long.valueOf(request.getParameter("authCode"));//谷歌身份验证器上输入的验证码

        }catch (Exception e) {
            logger.warn("输入二维码格式不对",e);
            return R.error("输入数字验证码");
        }
        String dynamicAuthToken = request.getParameter(AuthConst.DYNAMIC_AUTH_TOKEN_KEY);
        if(checkFormCache.containsKey(dynamicAuthToken)){
            logger.warn("操作太过频繁");
            return R.error("操作太过频繁");
        }
        checkFormCache.put(dynamicAuthToken,dynamicAuthToken,2000);
        if(!loginTempTokenMap.containsKey(dynamicAuthToken)){
            logger.warn("请先登录后在进行二次验证");
            return R.error("请先登录");
        }
        TempTokenEntity tempTokenEntity = loginTempTokenMap.get(dynamicAuthToken,false);
        Long oldTime = tempTokenEntity.getTempExpireTime().getTime();
        Long nowTime = new Date().getTime();
        if(nowTime>oldTime){
            //防止直接访问generateCode 导致二次验证被解绑，而被非法操作绑定
            logger.warn("二次验证已过期，请返回上一步重新登陆");
            return R.error("二次验证已过期，请返回上一步重新登陆");
        }
        SysUser sysUser = null;
        try{
            sysUser =  sysUserService.getSysUserById(loginMap.get(dynamicAuthToken,false).getUserId());
            if(null == sysUser){
                logger.error("账号不存在");
                return R.error("账号不存在");
            }
            boolean authResult = googleAuthenticator.checkCode(authCode,sysUser.getTagKey());//检验用户输入的验证码是否正确
            logger.info("{}请求二次验证,验证码是{},否正确：{}",sysUser.getUsername(),authCode,authResult);
            if(!authResult) {
                logger.warn("{}验证码错误:{}",sysUser.getUsername(),authCode);
                return R.error("验证码错误");
            }
        }catch (Exception e) {
            logger.error("二次验证登录系统异常",e);
            return R.error();
        }
        try{
            if(null!=sysUser){
                SysUser updateSysUser = new  SysUser();
                updateSysUser.setTagNum(sysUser.getTagNum().longValue()+1);//修改登录次数
                sysUserService.update(updateSysUser,new LambdaQueryWrapper<SysUser>().eq(SysUser::getUserId,sysUser.getUserId()));
            }
            loginMap.remove(dynamicAuthToken);
            loginTempTokenMap.remove(dynamicAuthToken);
        }catch(Exception e){
            logger.error("update TgNum次数出错:{} user:{}",e,sysUser);
        }
        UserInfoInTokenBO userInfoInToken = new UserInfoInTokenBO();
        userInfoInToken.setUserId(String.valueOf(sysUser.getUserId()));
        userInfoInToken.setSysType(SysTypeEnum.ADMIN.value());
        userInfoInToken.setEnabled(sysUser.getStatus() == 1);
        userInfoInToken.setPerms(getUserPermissions(sysUser.getUserId()));
        userInfoInToken.setNickName(sysUser.getUsername());
        userInfoInToken.setShopId(sysUser.getShopId());
        TokenInfoVO tokenInfoVO = tokenStore.storeAndGetVo(userInfoInToken);
        return R.ok().put("data",tokenInfoVO);
    }
    private Set<String> getUserPermissions(Long userId) {
        List<String> permsList;

        //系统管理员，拥有最高权限
        if(userId == Constant.SUPER_ADMIN_ID){
            List<SysMenu> menuList = sysMenuService.list(Wrappers.emptyWrapper());
            permsList = menuList.stream().map(SysMenu::getPerms).collect(Collectors.toList());
        }else{
            permsList = sysUserService.queryAllPerms(userId);
        }
        return permsList.stream().flatMap((perms)->{
                    if (StrUtil.isBlank(perms)) {
                        return null;
                    }
                    return Arrays.stream(perms.trim().split(StrUtil.COMMA));
                }
        ).collect(Collectors.toSet());
    }
}
